Skip to content

fix(aggregator): attribute a midnight-straddling turn's By Activity to today on the all-provider view - #878

Merged
iamtoruk merged 1 commit into
mainfrom
fix/by-activity-straddle-undercount
Aug 3, 2026
Merged

fix(aggregator): attribute a midnight-straddling turn's By Activity to today on the all-provider view#878
iamtoruk merged 1 commit into
mainfrom
fix/by-activity-straddle-undercount

Conversation

@iamtoruk

@iamtoruk iamtoruk commented Aug 3, 2026

Copy link
Copy Markdown
Member

What

A deep audit of the recently-merged batch surfaced one real regression introduced by #853 (midnight-straddling turn slicing): on the all-provider, multi-day headline, a turn spanning local midnight lost the post-midnight half of its By Activity total and its daily turn count, even though the headline cost, calls, By Model, and By Project were all correct.

buildDurablePeriod derived today's slice of that view from the unsliced whole-range parse (src/usage-aggregator.ts), so the straddling turn kept its category and turn count anchored on its yesterday start. Call-derived cost/calls bucketed onto today correctly (per #853), but the turn-level category stayed on yesterday and the union then took today from a slice with an empty category map.

Fix

Slice the today parse with filterProjectsByDays(raw, {today}) first — the same re-anchoring the provider-filtered path already relies on. That moves the straddling turn's surviving today calls (and its category) onto today. Because day-aggregator accumulates category cost as the sum of the slice's own calls, day-N + day-N+1 still equals the whole-range total (no over-count); the per-day turn-count split matches the cache side and the documented per-day semantics.

Verification

  • The bug and the fix, measured through the real cache+live union (isolated dirs, seeded daily cache): before, By Activity summed to $0.45 of a $1.35 straddling turn and today's daily row read turns: 0 with calls: 1; after, By Activity sums to $1.35 and today carries turns: 1.
  • No over-count / conservation intact: 27 conservation checks (cost/calls/tokens/project-filter/watermark) all pass on the fix.
  • Regression test added to the midnight-straddling turn conservation suite, mutation-checked (fails on the pre-fix code, passes with it).
  • tsc clean; full CLI suite green apart from the two pre-existing parser.test.ts failures that reproduce on main.

Also

Fills in the CHANGELOG.md ## Unreleased entries that were missing for the batch (#853, #856, #872, #846/#859, #866/#867, plus a #833 citation).

Found via a post-merge audit; headline money was never wrong, so this is a By-Activity / daily-turn-count accuracy fix, not a total.

…he all-provider view

buildDurablePeriod derived the today slice of the multi-day, all-provider
headline from the unsliced whole-range parse, so a turn spanning local midnight
kept its category and turn count anchored on its yesterday start. The per-call
cost and calls bucketed onto today correctly, but By Activity and the JSON
daily turn count lost the post-midnight half — categories summed to only the
pre-midnight cost while the headline, By Model and By Project were right.

Slice the today parse with filterProjectsByDays first, which re-anchors the
straddling turn to its surviving today calls, so today's category cost lands on
today. Category cost is the sum of the slice's own calls, so day-N + day-N+1
still equals the whole-range total (no over-count); the per-day turn-count
split matches the cache side and the documented per-day semantics.

Adds a regression test in the straddling-turn conservation suite
(mutation-checked: fails on the pre-fix code). Also fills in the CHANGELOG
Unreleased entries for the batch (#853, #856, #872, #846/#859, #866/#867, #833).
@iamtoruk
iamtoruk force-pushed the fix/by-activity-straddle-undercount branch from a2167a5 to dbdbf46 Compare August 3, 2026 20:46
@iamtoruk
iamtoruk merged commit 519f0a0 into main Aug 3, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant